package cn.amarone.wineblog.config.security;

import cn.amarone.wineblog.common.appointment.Result;
import cn.amarone.wineblog.util.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * Spring Security 提供有若干个过滤器，它们能够拦截 Servlet * 请求，
 * 并将这些请求转给认证和访问决策管理器处理，从而增强安全性。用户可以根据自己的需要，使用适当的过滤器来保护自己的应用程序。
 * WebSecurityConfig类与注释 @EnableWebSecurity，使Spring Security的网络安全支持，并提供了Spring MVC的整合。 它还扩展
 * WebSecurityConfigurerAdapter并覆盖了其一些方法来设置Web安全配置的某些细节。
 */
// @EnableWebSecurity允许Spring查找该类并将其自动应用于全局Web安全。@EnableWebSecurity如果禁用默认的安全配置注解是至关重要的。

/** Spring Security 就是一大堆责任链 要想实现自己自定义的filter 比如 要实现1, 就得按照1的规则实现自动的定义并把1在责任链中的位置替换为自定义的filter */
@Configuration
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired private UserDetailsImpl userDetailsImpl;

  /**
   * 密码加密bean
   *
   * @return
   */
  @Bean
  PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsImpl);
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    web.ignoring()
        .antMatchers(
            "/css/**",
            "/js/**",
            "/index.html",
            "/img/**",
            "/fonts/**",
            "/favicon.ico",
            "/doLogin",
            "/captcha");
  }

  @Bean
  CustomAuthenticationFilter loginFilter() throws Exception {
    CustomAuthenticationFilter loginFilter = new CustomAuthenticationFilter();
    loginFilter.setAuthenticationSuccessHandler((request, response, authentication) -> {
      log.debug("login success");
    });
    loginFilter.setAuthenticationFailureHandler(
        (request, response, exception) -> {
          ServletUtils.renderString(response, Result.error(exception.getMessage()).toString());
        });
    loginFilter.setAuthenticationManager(authenticationManagerBean());
    loginFilter.setFilterProcessesUrl("/doLogin");
    return loginFilter;
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.csrf()
        .and()
        .cors()
        .disable()
        .authorizeRequests()
        .and()
        .logout()
        .logoutSuccessHandler((req, resp, authentication) -> {})
        .permitAll()
        .and()
        .exceptionHandling()
        // 没有认证时，在这里处理结果，不要重定向
        .authenticationEntryPoint((req, resp, authException) -> {
          ServletUtils.renderString(resp, Result.error(authException.getMessage()).toString());
        });

    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);

    // 在指定筛选器类的位置添加筛选器
    http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
  }
}
